package org.example.lock;

/*
    传统上，我们可以通过synchronized关键字+wait+notify/notifyA11来实现多个线程之间的协调与通信，整个过程都是由JVM来帮助
    我们实现的；开发者无需(也是无法)了解底层的实现细节

    从JDK 5开始，并发包提供了Lock,  condition(await与signal/signalA11)来实现多个线程之间的协调与通信，整个过程都是由开发者来
    控制的，而且相比于传统方式，更加灵活，功能也更加强大

    Thread.sleep与await(或是object的wait方法)的本质区别：
        sleep方法本质上不会释放锁，而await会释放锁，并且在signal后，还需要重新获得锁才能继续执行(该行为与Object的wait方法完全一致)
*/

import java.util.Arrays;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;

/*
* 示例基于Condition文档的示例进行编写
* */
public class ConditionSty {
    private String[] elements =new String[10];
    private Lock lock =new ReentrantLock();
    // 定义两个条件变量，分别表示队列不满与不空
    // 这两个条件变量都是依赖于同一个锁的，即lock
    // notEmptyCondition 取数据
    private Condition notEmptyCondition =lock.newCondition();
    // notFullCondition 放数据
    private Condition notFullCondition =lock.newCondition();
    private int elementCount;//elements数组中已有的元素数量
    private int putIndex;
    private int takeIndex;

    public void put(String element)throws InterruptedException {
        this.lock.lock();
        try{
            // 队列满，等待
            while(this.elementCount == this.elements.length){
                notFullCondition.await();
            }
            elements[putIndex]=element;
            if(++putIndex==this.elements.length){
                putIndex=0;
            }
            ++elementCount;
            System.out.println("put method:"+ Arrays.toString(elements));
            notEmptyCondition.signal();
        }finally {
            this.lock.unlock();
        }
    }

    public String take()throws InterruptedException {
        this.lock.lock();
        try{
            // 队列空，等待
            while(this.elementCount==0){
                notEmptyCondition.await();
            }
            String element =elements[takeIndex];
            elements[takeIndex]=null;
            if(++takeIndex==this.elements.length){
                takeIndex=0;
            }
            --elementCount;
            System.out.println("take method:"+ Arrays.toString(elements));
            notFullCondition.signal();
            return element;
        }finally {
            this.lock.unlock();
        }
    }

    public static void main(String[] args) {
        ConditionSty conditionSty =new ConditionSty();
        //IntStream创建10个线程，模拟放数据
        IntStream.range(0,10).forEach(i-> new Thread(()->{
                try {
                    conditionSty.put("element"+i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }).start());

        // 使用IntStream创建10个线程，模拟取数据
        IntStream.range(0,10).forEach(i-> new Thread(()->{
                try {
                    String e = conditionSty.take();
//                    System.out.println("take element:"+e);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }).start());

    }
}
